package grpcclient

import (
	"encoding/json"
	"fmt"
	leafv1 "gitee.com/guolianyu/proto/leaf/service/v1"
	"gitee.com/guolianyu/proto/service"
	userv1 "gitee.com/guolianyu/proto/user/service/v1"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/middleware"
	"github.com/go-kratos/kratos/v2/middleware/logging"
	"github.com/go-kratos/kratos/v2/middleware/metadata"
	"github.com/go-kratos/kratos/v2/middleware/metrics"
	"github.com/go-kratos/kratos/v2/middleware/recovery"
	"github.com/go-kratos/kratos/v2/middleware/tracing"
	"github.com/go-kratos/kratos/v2/registry"
	"github.com/go-kratos/kratos/v2/transport/grpc"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/trace"
	"golang.org/x/net/context"
	"reflect"
	"time"
)

/*
	服务发现client声明
*/

var defaultTimeOut = 20 * time.Second

// GetDefaultOptions 默认grpc客户端配置
func GetDefaultOptions(r registry.Discovery, serviceName string) []grpc.ClientOption {
	return []grpc.ClientOption{
		grpc.WithMiddleware(
			tracing.Client(),
			recovery.Recovery(),
			logging.Client(log.GetLogger()),
			metadata.Client(),
			metrics.Client(),
			func(handler middleware.Handler) middleware.Handler {
				return func(ctx context.Context, req interface{}) (res interface{}, err error) {
					res, err = handler(ctx, req)
					trace.SpanFromContext(ctx).SetAttributes(
						attribute.String("param", toJSON(req)),
						attribute.String("response", toJSON(res)),
						attribute.String("error", fmt.Sprint(err)),
					)
					return
				}
			},
		),
		grpc.WithTimeout(defaultTimeOut),
		grpc.WithEndpoint("discovery:///" + serviceName),
		grpc.WithDiscovery(r),
	}
}

// NewUserServiceClient user服务发现声明
func NewUserServiceClient(r registry.Discovery) userv1.UserClient {
	options := GetDefaultOptions(r, service.UserService)
	conn, err := grpc.DialInsecure(
		context.Background(),
		append(options,
			grpc.WithTimeout(20*time.Second),
		)...,
	)
	if err != nil {
		panic(err)
	}
	return userv1.NewUserClient(conn)
}

// NewLeafSnowflakeClient 分布式ID生成器
func NewLeafSnowflakeClient(r registry.Discovery) leafv1.LeafSnowflakeServiceClient {
	options := GetDefaultOptions(r, service.LeafService)
	conn, err := grpc.DialInsecure(
		context.Background(),
		append(options,
			grpc.WithTimeout(20*time.Second),
		)...,
	)
	if err != nil {
		panic(err)
	}
	return leafv1.NewLeafSnowflakeServiceClient(conn)
}

func toJSON(v interface{}) string {
	if v == nil {
		return "{}"
	}
	value := reflect.ValueOf(v)
	switch value.Kind() {
	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
		if value.IsNil() {
			return "{}"
		}
	}
	switch v.(type) {
	case string:
		return v.(string)
	}
	b, _ := json.Marshal(v)
	return string(b)
}
